package cn.kz.hrm.service.impl;

import cn.kz.hrm.commonfeiclient.CommonFeignClient;
import cn.kz.hrm.domain.CourseType;
import cn.kz.hrm.mapper.CourseTypeMapper;
import cn.kz.hrm.service.ICourseTypeService;
import cn.kz.hrm.util.AjaxResult;
import cn.kz.hrm.vo.CrumbsVo;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.xml.transform.Result;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 课程目录 服务实现类
 * </p>
 *
 * @author kz
 * @since 2020-06-29
 */
@Service
public class CourseTypeServiceImpl extends ServiceImpl<CourseTypeMapper, CourseType> implements ICourseTypeService {

    @Autowired
    private CommonFeignClient commonFeignClient;
    //打印日志，是为了方便测试出现问题的原因及地址。
    private Logger log = LoggerFactory.getLogger(CourseTypeServiceImpl.class);

    //方式一：利用数据库的特性映射查询，是数据层现有结构性的数据，
    // 但是性能相对较低，xml中出现的循环，实现小数据的查询，
   /* @Override
    public List<CourseType> getTreeData() {
       return baseMapper.getTreeData();
    }*/


    /**
     * 方式二：从数据库中查到所有的数据，然后利用循环或者lambda进行组装数据结构。
     * 获取树结构的课程数据。同时，使用redis 对数据进行缓存。
     * @return
     */
    @Override
    public List<CourseType> getTreeData() {
        //插叙数据，查看是否在缓存中存在，否则去数据库中进行查询，然后从存储一份到redis中，最后再进行返回，做结构处理。
        List<CourseType> courseTypes = getCourseTypesAndSaveRedis();
        //把课程分类变成有层级结构的数据返回
        return treeCourseTypeHandler(courseTypes);
    }



    /**
     * 获取树结构的课程数据
     * @param courseTypes
     * @return
     */
    private List<CourseType> treeCourseTypeHandler(List<CourseType> courseTypes) {
        //创建数组装最高一层的数据。
        List<CourseType> resultTypes = new ArrayList<>();
        //2.利用循环组装数据成有层级结构。1级下面有2级，2级下面有3级的数据结构。
        courseTypes.forEach(courseType -> {
            if (courseType.getPid().longValue() == 0) {//找出一级的数据，pid=0，证明是一级结构。
                resultTypes.add(courseType);//添加一级数据
            } else {
                //其他的所有分类找各自的父级分类，谁的pid是有对应的id，则证明是该id是pid的上一级
                CourseType parentType = null;//获取父级的id
                for (CourseType tempSonType : courseTypes) {
                    //如果某个分类的ID等于当前分类的PID谁就是其对应的上一级
                    if (tempSonType.getId().equals(courseType.getPid())) {
                        parentType = tempSonType;
                        break;
                    }
                }
                //把当前分类加入当前分类的父分类
                parentType.getChildren().add(courseType);
            }
        });
        return resultTypes;
    }

    /**
     * 查询数据到以及存储redis
     * 1、先查询是否在redis中是否有数据
     * 1.1 如果有数据就将数据返回
     * 1.2 如果没有数据将去数据库中查询数据
     * 2、在数据库中查询数据之后将数据存储到redis中一份，然后染回
     * 3、第二次查询的时候先去数据库中进行查询，然后再判断是否有数据，对数据的修改-添加-删除，都应该要重置redis。
     *
     * @return
     */
    private List<CourseType> getCourseTypesAndSaveRedis() {
        //根据key去redis中查询value
        AjaxResult courseTypeStr = commonFeignClient.getRedis("KEY_COURSE_TYPE");
        if (courseTypeStr.getResultObj() != null && courseTypeStr.isSuccess()) { //证明是redis中存在。转化数据类型类型进行转换，
            //存储进去的json数据，需要转换成集合对象
            String resultObj = courseTypeStr.getResultObj().toString();
            List<CourseType> courseTypes = JSON.parseArray(resultObj, CourseType.class);
            return courseTypes;
        } else {
            //@todo 去数据库中查询 水平查询所有的数据。
            List<CourseType> courseTypes = baseMapper.selectList(null);
            String courseTypesString = JSON.toJSONString(courseTypes);
            AjaxResult key_course_type = commonFeignClient.setRedis("KEY_COURSE_TYPE", courseTypesString);
            return courseTypes;
        }
    }

    /**
     * 重写insert方法，因为在插入的时候，需要重写他的redis方法。
     * @param entity
     * @return
     */
    @Override
    public boolean insert(CourseType entity) {
        boolean result = super.insert(entity);
        //如果插入成功，就需要删除redis。重置redis。
        if(result){ //添加成功，重置redis。
            deleteRedis();
        }
        return result;
    }

    /**
     * 根据id进行修改数据，然后重置数据。
     * @param entity
     * @return
     */
    @Override
    public boolean updateById(CourseType entity) {
       boolean result = super.updateById(entity);
        if(result){ //添加成功，重置redis。
            deleteRedis();
        }
        return result;
    }

    /**
     * 根据id进行删除数据，成功则重置数据
     * @param id
     * @return
     */
    @Override
    public boolean deleteById(Serializable id) {
        boolean result = super.deleteById(id);
        if(result){ //添加成功，重置redis。
            deleteRedis();
        }
        return result;
    }

    /**
     * 删除redis。
     */
    private void deleteRedis() {
        AjaxResult ajaxResult = commonFeignClient.deleteRedis("KEY_COURSE_TYPE");
        if(!ajaxResult.isSuccess()){
            //清除缓存失败，如果可以存在脏数据的话，就不用抛出异常，不能就抛出异常。让事务回滚
        }
    }

    /**
     * 获取面包屑
     * @param id
     * @return
     */
    @Override
    public List<CrumbsVo> getCrumbs(Long id) {
        //获取收集
        CourseType courseType = baseMapper.selectById(id);
        //获取path
        String path = courseType.getPath();

        //对该路径上最前面和最后面的“.”去除
        path = path.startsWith(".")? path.substring(1) :path ;
        //这个path没有了.，在上一步进行了切除
        path = path.endsWith(".")? path.substring(0,path.length()-1) :path ;

        //将ipath路径进行切割。根据“.”进行分割
        String[] idsStr = path.split("\\.");//注意 . 是正则表达式中有特殊含义所以需要对其进行转义
        //获取将string类型的ids进行转换成long类型的ids，使用循环进行，利用循环
        List<Long> ids = new ArrayList<>();
        for (String idString :idsStr) { //将string类型id转换成long类型的id，同时添加到新的list集合中
            ids.add(Long.valueOf(idString));
        }

        //返回的数据结构其中包含有OrtherCourseTypes，OwnerProductType
        List<CrumbsVo> crumbsVos = new ArrayList<>();

        //查询所有的ids中的数据,批量查询
        List<CourseType> courseTypes = baseMapper.selectBatchIds(ids);
        //利用循环获取该id所对应的courseType
        for (CourseType courseType1: courseTypes) {//这个循环是treeData结构的数据，所以每次删除本身只会删除一个。
            //设置给自己
            CrumbsVo crumbsVo = new CrumbsVo();
            crumbsVo.setOwnerProductType(courseType1);

            //根据类型的pid进行查询，获得pid相同的类型的集合,其中该数据中包括自己本身
            List<CourseType> allOrtherCourseTypes = baseMapper.findByPid(courseType1.getPid());

            //从allOrtherCourseTypes里面删除自己本身。因为ownerProductType里面已经存在了一个本身，重复
            //但是remove方法，比较的是对象的引用地址，这里只要比较id是否相同。所以从写equals方法。remove底层是equals方法。
            allOrtherCourseTypes.remove(courseType1);
            crumbsVo.setOtherProductTypes(allOrtherCourseTypes);//参数中已经删除了自己的本身

            //将数据添加到
            crumbsVos.add(crumbsVo);
        }
        return crumbsVos;
    }


}
